图说Linux进程之三
五、计算机体系结构相关数据
在task_struct里面有一个thread_info
为什么需要单独的这个数据结构呢?因为不同的体系结构可能会有不同的实现。
从代码可以看出不同的体系结构有不同的实现。
在thread_info里面有一个指针指向task_struct。
这个指针有什么用的,当一个用户态的进程进入到内核的时候,如何找到对应的task_struct呢?
一般是从当前CPU的一个寄存器里面,通过函数current_thread_info得到在内核态里面的thread_info的地址,然后就可以通过指针找到task_struct了。
六、进程树
task_struct中有一系列指针是用来维护进程树的。
parent指向的是一个进程的原来的父进程,real_parent指向的是进程的当前的父进程,这两个值大多数情况下是一致的。
但是有一种情况下不一致,就是在一个进程被Debug的时候,这个时候GDB就变成了当前的父进程。
父进程有一个children指针指向子进程的列表,里面有两个指针,一个指头,一个指尾。
同一个层次的进程通过sibing链表串起来。
七、进程的资源管理
一个进程是一系列资源的集合,主要有哪些资源呢?
一个是files,指向这个进程打开的所有的文件,具体可参考Linux的虚拟文件系统VFS。
一个是fs,指向文件系统相关的信息,例如进程的当前目录等,也可参考上面的那篇文章。
一个是sighand,指向用于处理信号的signal handler
一个是mm,指向这个进程的内存。
八、fork创建子进程
通过调用系统调用fork可以创建另一个进程。
会在内核里面调用_do_fork
里面会调用
p = copy_process(clone_flags, stack_start, stack_size, child_tidptr, NULL, trace, tls, NUMA_NO_NODE);
里面有
p = dup_task_struct(current, node);
retval = copy_creds(p, clone_flags);
cgroup_fork(p);
retval = copy_files(clone_flags, p);
retval = copy_fs(clone_flags, p);
retval = copy_sighand(clone_flags, p);
retval = copy_signal(clone_flags, p);
retval = copy_mm(clone_flags, p);
retval = copy_namespaces(clone_flags, p);
retval = copy_io(clone_flags, p);
retval = copy_thread_tls(clone_flags, stack_start, stack_size, p, tls);
pid = alloc_pid(p->nsproxy->pid_ns_for_children);
子进程有独立的task_struct结构,有自己的PID,在fork的当下,代码段是和父进程是一样的,一般会被exec系列函数重新加载新的代码段,数据段和栈是新创建的,采取的copy on write的模式。
上面提到的四个数据结构,都是完全拷贝一份的。
九、pthread_create创建新线程
新的线程和原来的线程是共享进程空间的,因而调用__clone函数。
在内核中调用sys_clone函数,但是会设定以下的flag
CLONE_VM,则子线程和父线程共享虚拟内存
CLONE_FILES,共享files
CLONE_FS,共享fs
CLONE_SIGHAND,共享sighand。